package redis

import (
	"errors"
	"github.com/gomodule/redigo/redis"
	"strings"
)

func (k *key) Multi(cmds ...any) ([]Result, error) {
	var cmdList []*cmd
	for _, c := range cmds {
		if cc, ok := c.(*cmd); ok {
			cmdList = append(cmdList, cc)
		} else {
			return nil, errors.New("Multi expects *cmd")
		}
	}
	conn := k.c.pool.Get()
	defer conn.Close()

	conn.Send("MULTI")
	for _, cmd := range cmdList {
		if err := conn.Send(cmd.cmd, cmd.args...); err != nil {
			return nil, err
		}
	}
	reply, err := conn.Do("EXEC")
	if err != nil {
		return nil, err
	}
	var rows []Result
	for i, r := range reply.([]any) {
		c := cmdList[i]
		if e, ok := r.(error); ok {
			c.err = e
		} else {
			c.reply = r
		}
		rows = append(rows, c)
	}
	return rows, nil
}

type ScanArgs struct {
	Pattern string
	Count   int
	Walk    func(string) bool
}

func (k *key) scan(cmd string, args *ScanArgs) error {
	conn := k.c.pool.Get()
	defer conn.Close()

	var ss []any

	curIndex := 0
	if k.key != "" {
		ss = append(ss, k.key)
		curIndex = 1
	}
	ss = append(ss, 0)

	if args.Pattern != "" {
		ss = append(ss, "MATCH", args.Pattern)
	}
	if args.Count == 0 {
		args.Count = 1000
	}
	ss = append(ss, "COUNT", args.Count)

	cur := 0
	for {
		ss[curIndex] = cur
		vs, err := redis.Values(conn.Do(cmd, ss...))
		if err != nil {
			return err
		}
		cur, _ = redis.Int(vs[0], nil)
		keys, _ := redis.Strings(vs[1], nil)
		for _, kk := range keys {
			if !args.Walk(kk) {
				return nil
			}
		}
		if cur == 0 {
			break
		}
	}
	return nil
}

func (k *key) Scan(args *ScanArgs) error {
	if k.c.prefix != "" {
		if args.Pattern == "" {
			args.Pattern = "*"
		}
		p := k.c.prefix + k.c.separator
		args.Pattern = p + args.Pattern
		walk := args.Walk
		args.Walk = func(kk string) bool {
			if !strings.HasPrefix(kk, p) {
				return true
			}
			return walk(kk[len(p):])
		}
	}
	return k.scan("SCAN", args)
}

func (k *key) Keys(pattern string) StringListResult {
	var keys []any
	if err := k.Scan(&ScanArgs{Pattern: pattern, Walk: func(key string) bool {
		keys = append(keys, key)
		return true
	}}); err != nil {
		return cmdErr(err)
	}
	return &cmd{reply: keys}
}
